<?php

abstract class Jf_View_Block_Tree_Abstract {

    protected $_container;

    protected $_minDepth;

    protected $_maxDepth;

    protected $_indent = '';

    protected $_acl;

    protected $_role;

    protected $_useAcl = true;

    public function setContainer (Jf_Tree_Container $container = null) {
        $this -> _container = $container;
        return $this;
    }

    public function getContainer () {
        if (null === $this -> _container) {
            if (Jf_Registry::isRegistered ('Backend_Navigation')) {
                $nav = Jf_Registry::get ('Backend_Navigation');
                if ($nav instanceof Jf_Tree_Container) {
                    return $this -> _container = $nav;
                }
            }
        }
        return $this -> _container;
    }

    public function setMinDepth ($minDepth = null) {
        if (null === $minDepth || is_int ($minDepth)) {
            $this -> _minDepth = $minDepth;
        } else {
            $this -> _minDepth = (int) $minDepth;
        }
        return $this;
    }

    public function getMinDepth () {
        if ( ! is_int ($this -> _minDepth) || $this -> _minDepth < 0) {
            return '';
        }
        return $this -> _minDepth;
    }

    public function setMaxDepth ($maxDepth = null) {
        if (null === $maxDepth || is_int ($maxDepth)) {
            $this -> _maxDepth = $maxDepth;
        } else {
            $this -> _maxDepth = (int) $maxDepth;
        }
        return $this;
    }

    public function getMaxDepth () {
        return $this -> _maxDepth;
    }

    public function setIndent ($indent) {
        $this -> _indent = $this -> _getWhitespace ($indent);
        return $this;
    }

    public function getIndent () {
        return $this -> _indent;
    }

    protected function _getWhitespace ($indent) {
        if (is_int ($indent)) {
            $indent = str_repeat (' ', $indent);
        }
        return (string) $indent;
    }

    public function __call ($method, array $arguments = array()) {
        return call_user_func_array (array($this -> getContainer (), $method), $arguments);
    }

    public function toString () {
        try {
            return $this -> render ();
        } catch (Exception $e) {
            $msg = get_class ($e) . ': ' . $e -> getMessage ();
            trigger_error ($msg, E_USER_ERROR);
            return '';
        }
    }

    public function __toString () {
        return $this -> toString ();
    }

    public function findActive (Jf_Tree_Container $container, $minDepth = null, $maxDepth = -1) {
        if ( ! is_int ($minDepth)) {
            $minDepth = $this -> getMinDepth ();
        }
        if (( ! is_int ($maxDepth) || $maxDepth < 0) && null !== $maxDepth) {
            $maxDepth = $this -> getMaxDepth ();
        }
        $found = null;
        $foundDepth =  - 1;
        $iterator = new RecursiveIteratorIterator ($container, RecursiveIteratorIterator::CHILD_FIRST);
        foreach ($iterator as $node) {
            $currDepth = $iterator -> getDepth ();
            if ($currDepth < $minDepth) { // || !$this->accept($node)) {
                continue;
            }
            if ($node -> isActive (false) && $currDepth > $foundDepth) {
                $found = $node;
                $foundDepth = $currDepth;
            }
        }
        if (is_int ($maxDepth) && $foundDepth > $maxDepth) {
            while ($foundDepth > $maxDepth) {
                if ( -- $foundDepth < $minDepth) {
                    $found = null;
                    break;
                }
                $found = $found -> getParent ();
                if ( ! $found instanceof Jf_Tree_Node) {
                    $found = null;
                    break;
                }
            }
        }
        if ($found) {
            return array('node' => $found, 'depth' => $foundDepth);
        } else {
            return array();
        }
    }

    public function accept (Jf_Tree_Node $node, $recursive = true) {
        // accept by default
        $accept = true;
        if ( ! $node -> isVisible (false)) {
            $accept = false;
        } elseif ($this -> getUseAcl () &&  ! $this -> acceptAcl ($node)) {
            $accept = false;
        }
        if ($accept && $recursive) {
            $parent = $node -> getParent ();
            if ($parent instanceof Jf_Tree_Node) {
                $accept = $this -> accept ($parent, true);
            }
        }
        return $accept;
    }

    public function htmlify (Jf_Tree_Node $node) {
        // get label and title for translating
        $label = $node -> getLabel ();
        $title = $node -> getTitle ();
        $href = $node -> getHref ();
        $target = $node -> getTarget ();
        $id = $node -> getId ();
        // get attribs for element
        $attribs = " id=\"$id\"";
        $attribs .= " title=\"$title\"";
        // does node have a href?
        if ($href) {
            $element = 'a';
            $attribs .= " href=\"$href\"";
            $attribs .= " target=\"$target\"";
        } else {
            $element = 'span';
        }
        return '<' . $element . $attribs . '>' . $label . '</' . $element . '>';
    }

    protected function acceptAcl (Jf_Tree_Node $node) {
        if ( ! $acl = $this -> getAcl ()) {
            return true;
        }
        $role = $this -> getRole ();
        $resource = $node -> getResource ();
        $privilege = $node -> getPrivilege ();
        if ($resource || $privilege) {
            return $acl -> isAllowed ($role, $resource, $privilege);
        }
        return true;
    }

    public function setUseAcl ($useAcl = true) {
        $this -> _useAcl = (bool) $useAcl;
        return $this;
    }

    public function getUseAcl () {
        return $this -> _useAcl;
    }

    public function setAcl (Zend_Acl $acl = null) {
        $this -> _acl = $acl;
        return $this;
    }

    public function getAcl () {
        return $this -> _acl;
    }

    public function setRole ($role = null) {
        if (null === $role || is_string ($role) || $role instanceof Zend_Acl_Role_Interface) {
            $this -> _role = $role;
        } else {
            $e = new Jf_Exception (sprintf ('$role must be a string, null, or an instance of ' . 'Zend_Acl_Role_Interface; %s given', gettype ($role)));
            $e -> setView ($this -> view);
            throw $e;
        }
        return $this;
    }

    public function getRole () {
        return $this -> _role;
    }

    abstract function render ();

}
